﻿using Org.BouncyCastle.Utilities.Encoders;
using System;
using System.Security.Cryptography;
using System.Text;

namespace RuoVea.SM;

/// <summary>
/// 国密算法综合工具类
/// 提供SM2、SM3、SM4算法的便捷调用方法
/// </summary>
public static class SMEncryption
{
    #region SM2 非对称加密

    /// <summary>
    /// 生成SM2密钥对
    /// </summary>
    /// <returns>元组(公钥, 私钥)</returns>
    public static (string PublicKey, string PrivateKey) SM2GenerateKeyPair()
    {
        return SM2Crypt.GenerateKeyPair();
    }

    /// <summary>
    /// SM2加密
    /// </summary>
    /// <param name="plainText">明文</param>
    /// <param name="publicKey">16进制格式的公钥</param>
    /// <returns>加密后的16进制字符串</returns>
    public static string SM2Encrypt(string plainText, string publicKey)
    {
        if (string.IsNullOrEmpty(plainText))
            throw new ArgumentNullException(nameof(plainText));
        if (string.IsNullOrEmpty(publicKey))
            throw new ArgumentNullException(nameof(publicKey));

        byte[] sourceData = Encoding.UTF8.GetBytes(plainText);
        return SM2Crypt.Encrypt(Hex.Decode(publicKey), sourceData);
    }

    /// <summary>
    /// SM2解密
    /// </summary>
    /// <param name="cipherText">16进制格式的密文</param>
    /// <param name="privateKey">16进制格式的私钥</param>
    /// <returns>解密后的明文</returns>
    public static string SM2Decrypt(string cipherText, string privateKey)
    {
        if (string.IsNullOrEmpty(cipherText))
            throw new ArgumentNullException(nameof(cipherText));
        if (string.IsNullOrEmpty(privateKey))
            throw new ArgumentNullException(nameof(privateKey));

        return SM2Crypt.Decrypt(cipherText, privateKey);
    }

    #endregion

    #region SM3 哈希算法

    /// <summary>
    /// SM3哈希计算
    /// </summary>
    /// <param name="plainText">明文</param>
    /// <returns>64字符的16进制哈希值</returns>
    public static string SM3Hash(string plainText)
    {
        if (string.IsNullOrEmpty(plainText))
            throw new ArgumentNullException(nameof(plainText));

        return SM3Crypt.Encrypt(plainText);
    }

    /// <summary>
    /// 验证SM3哈希值
    /// </summary>
    /// <param name="plainText">明文</param>
    /// <param name="hashValue">待验证的哈希值</param>
    /// <returns>是否匹配</returns>
    public static bool SM3Verify(string plainText, string hashValue)
    {
        if (string.IsNullOrEmpty(plainText))
            throw new ArgumentNullException(nameof(plainText));
        if (string.IsNullOrEmpty(hashValue))
            throw new ArgumentNullException(nameof(hashValue));

        return SM3Crypt.Verify(plainText, hashValue);
    }

    #endregion

    #region SM4 对称加密
    private const int SM4KeySize = 16; // SM4密钥长度(16字节/128位)
    private const int SM4IvSize = 16;  // SM4初始向量长度(16字节)
    /// <summary>
    /// 生成随机SM4密钥
    /// </summary>
    /// <param name="hexFormat">是否返回16进制格式</param>
    /// <returns>16字节的随机密钥</returns>
    public static string SM4GenerateKey(bool hexFormat = false)
    {
        byte[] keyBytes = new byte[SM4KeySize];
        using (var rng = RandomNumberGenerator.Create())
        {
            rng.GetBytes(keyBytes);
        }

        return hexFormat ? Hex.ToHexString(keyBytes).ToUpper() : Encoding.ASCII.GetString(keyBytes);
    }

    /// <summary>
    /// 生成随机SM4初始向量(IV)
    /// </summary>
    /// <param name="hexFormat">是否返回16进制格式</param>
    /// <returns>16字节的随机IV</returns>
    public static string SM4GenerateIV(bool hexFormat = false)
    {
        byte[] ivBytes = new byte[SM4IvSize];
        using (var rng = RandomNumberGenerator.Create())
        {
            rng.GetBytes(ivBytes);
        }

        return hexFormat ? Hex.ToHexString(ivBytes).ToUpper() : Encoding.ASCII.GetString(ivBytes);
    }
    /// <summary>
    /// SM4 ECB模式加密
    /// </summary>
    /// <param name="plainText">明文</param>
    /// <param name="secretKey">密钥(16字节)</param>
    /// <param name="hexString">密钥是否为16进制格式</param>
    /// <returns>16进制格式的密文</returns>
    public static string SM4EncryptECB(string plainText, string secretKey = null, bool hexString = false)
    {
        if (string.IsNullOrEmpty(plainText))
            throw new ArgumentNullException(nameof(plainText));

        var sm4 = new SM4Crypt { HexString = hexString };
        return string.IsNullOrEmpty(secretKey)
            ? sm4.EncryptECB(plainText)
            : sm4.EncryptECB(plainText, secretKey);
    }

    /// <summary>
    /// SM4 ECB模式解密
    /// </summary>
    /// <param name="cipherText">16进制格式的密文</param>
    /// <param name="secretKey">密钥(16字节)</param>
    /// <param name="hexString">密钥是否为16进制格式</param>
    /// <returns>解密后的明文</returns>
    public static string SM4DecryptECB(string cipherText, string secretKey = null, bool hexString = false)
    {
        if (string.IsNullOrEmpty(cipherText))
            throw new ArgumentNullException(nameof(cipherText));

        var sm4 = new SM4Crypt { HexString = hexString };
        return string.IsNullOrEmpty(secretKey)
            ? sm4.DecryptECB(cipherText)
            : sm4.DecryptECB(cipherText, secretKey);
    }

    /// <summary>
    /// SM4 CBC模式加密
    /// </summary>
    /// <param name="plainText">明文</param>
    /// <param name="secretKey">密钥(16字节)</param>
    /// <param name="iv">初始向量(16字节)</param>
    /// <param name="hexString">密钥和IV是否为16进制格式</param>
    /// <returns>16进制格式的密文</returns>
    public static string SM4EncryptCBC(string plainText, string secretKey = null, string iv = null, bool hexString = false)
    {
        if (string.IsNullOrEmpty(plainText))
            throw new ArgumentNullException(nameof(plainText));

        var sm4 = new SM4Crypt { HexString = hexString };
        return string.IsNullOrEmpty(secretKey)
            ? sm4.EncryptCBC(plainText)
            : sm4.EncryptCBC(plainText, secretKey, iv ?? sm4.DefaultIV);
    }

    /// <summary>
    /// SM4 CBC模式解密
    /// </summary>
    /// <param name="cipherText">16进制格式的密文</param>
    /// <param name="secretKey">密钥(16字节)</param>
    /// <param name="iv">初始向量(16字节)</param>
    /// <param name="hexString">密钥和IV是否为16进制格式</param>
    /// <returns>解密后的明文</returns>
    public static string SM4DecryptCBC(string cipherText, string secretKey = null, string iv = null, bool hexString = false)
    {
        if (string.IsNullOrEmpty(cipherText))
            throw new ArgumentNullException(nameof(cipherText));

        var sm4 = new SM4Crypt { HexString = hexString };
        return string.IsNullOrEmpty(secretKey)
            ? sm4.DecryptCBC(cipherText)
            : sm4.DecryptCBC(cipherText, secretKey, iv ?? sm4.DefaultIV);
    }

    #endregion
}